#!/usr/bin/python
# -*- coding: utf-8 -*-

import simplejson
import MuConnection
import time
from StringIO import StringIO
import logging

from MuPython.exceptions import MuTestException

log = logging.getLogger('MuPython.MuTestApi')

""" MuTestApi contains the basic REST commands necessary to execute all Studio 
Verify functions 
    
Classes contained: 
MuTestApi - This class is instantiated to run Studio Verify commands and 
            recieves one argument:
    MuConnection - The connection class for maintaining a SSL session 
                   to the Mu Test Suite

TestCase: Not implemented yet: This class will contain a single line of 
          data to execute a test case with Studio Verify
    
Option: this class is a container for the Options used in verrifying 
            the scenario and in a TestCase
    
Result: this class is used to reperesent the result of exeacuting 
        a test case.
    
"""


class MuTestApi:

    """ MuTestApi: This class is instantiated to run Studio Verify 
                   commands and recieves one argument:

    Arguments: 
    MuConnection - The connection class for maintaining a SSL session 
                   to the Mu Test Suite

    Globals:
    muConnection - The SSL connection manager
    session - containes the active session id for this instance
    VERBOSE - enabel detaield printing to screen.
    
    TODO:
    logging - Used to loog activiteis instead of VERBOSE
    TestCase - a class that represents a single test case instance
    
    """

    muConnection = None
    session = None
    VERBOSE = True

    def __init__(self, connection):
        """ set up the TestAPI with the passed in MuConnection """

        self.muConnection = connection

    def newSession(self):
        """ initiate a new TestApi session for testing 
        Example URL: https://$MU_IP/api/v5/ddt/newSession
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + "/api/v5/ddt/newSession"
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def getAllSessions(self):
        """ Used to get a list of all active sessions on the Mu Test Suite. No 
        active Session is required
        Example URL: https://$MU_IP/api/v5/ddt/getAllSessions
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/getAllSessions"
        responseData = self.muConnection.get(url, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def closeAllSessions(self):
        """ Used to close all active sessions on the Mu Test Suite. No active 
        Session is required.

        Example URL: https://$MU_IP/api/v5/ddt/closeAllSessions
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/closeAllSessions"
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def closeSession(self):
        """ Used to close the active sessions on the Mu Test Suite. 
        An active Session is required

        Example URL: https://$MU_IP/api/v5/ddt/session/closeSession
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/closeSession"
        responseData = self.muConnection.get(url, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def loadScenario(self, uuid):
        """ Used to load a scenario into the active test session on the Mu. 
        The scenario must reside on the Mu prior to this commands execution.
        An active session is required.

        Example URL: 
        https://$MU_IP/api/v5/ddt/session/loadScenario/<scenario_uuid>
            
        Arguments:
            uuid - The uuid of the scenario on the Mu Test Suite
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/loadScenario/" + uuid
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def setHost(self, hostRole, hostName):
        """ Used to set the source and/or traget hosts in a scenario. 
        This must be done prior to scenario verification or a test run. 
        An active session is required.

        Example URL: 
        https://$MU_IP/api/v5/ddt/session/scenario/hosts/<host_role_1>/<host_name_1>
            
        Arguments:
            hostRole - The host tole specified in the scenario
            hostName - The name of the host, a Mu interface or a host 
                from the host table 
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/scenario/hosts/" + hostRole + "/" + \
            hostName
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def getHosts(self):
        """ Used to get the host roles in a scenario. An active session 
        is required.
        Example URL: https://$MU_IP/api/v5/ddt/session/scenario/hosts
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/scenario/hosts"
        log.debug("URL: %s", url)
        responseData = self.muConnection.get(url, True)
        if responseData.getRetrievedObjectType() == responseData.JSON and \
            not responseData.hasError():
            jsonData = responseData.getJsonObject()
            log.debug("JSON Response: %s", jsonData)
            tmpResponse = jsonData["hosts"]
            log.debug("Host: %s", tmpResponse['hosts'])
            hostListData = tmpResponse["host"]
            responseData.setRetrievedObject(hostListData)
            responseData.setRetrievedObjectType(responseData.LIST)
            log.debug(responseData)
        else:
            log.debug(responseData)
            raise MuTestException
        self.session = responseData
        return responseData

    def getChannels(self):
        """ Used to get the channel names in a scenario if any exists. 
        An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/scenario/channels
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/scenario/channels"
        responseData = self.muConnection.get(url, True)
        if responseData.getRetrievedObjectType() == responseData.JSON and \
            not responseData.hasError():
            jasonData = responseData.getJsonObject()
            tmpResponse = jasonData["channels"]
            if tmpResponse != '':
                channelListData = tmpResponse["channel"]
                responseData.setRetrievedObject(channelListData)
                responseData.setRetrievedObjectType(responseData.LIST)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def setChannels(self, channelName, hostName):
        """ Used to set the channel hosts in a scenario. If channes are 
        present this must be done prior to a verification or a test suite 
        run. An active session is required.

        Example URL: 
        https://$MU_IP/api/v5/ddt/session/scenario/channels/<channelName>/<hostName>
            
        Arguments:
            channelName - the name of the channel specified in he sc senario. 
                normaly channel, channel_2, etc.
            hostName - the name of the host as it occurs in the Mu Netowrk 
                Host table
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/scenario/channels/" + channelName + "/" + \
            hostName
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def getOptions(self):
        """ Used to get the scenario options list from the loaded scenario. 
        An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/scenario/options
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/scenario/options"
        responseData = self.muConnection.get(url, True)
        if responseData.getRetrievedObjectType() == responseData.JSON and \
            not responseData.hasError():
            jasonData = responseData.getJsonObject()
            tmpResponse = jasonData["options"]
            if tmpResponse != '':
                optionListData = tmpResponse["option"]
                responseData.setRetrievedObject(optionListData)
                responseData.setRetrievedObjectType(responseData.LIST)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def setOptions(self, optionName, value):
        """ Used to set the scenario options list in the loaded scenario. The 
        scenario should load with the defaults but they can be altered to new 
        values and then verrified. An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/scenario/options/<optionName>/<value>
            
        Arguments:
            optionName - the full name of the option as 
                it occures in the scenario. for example: 
                scenario_user_options.my_option_name
            value - the value to assigne to the option.
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/scenario/options/" + optionName + "/" + \
            value
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def setupTest(self):
        """ Used to set the test peramitors in the memory of the Mu Test Suite.
         This must be done before any test runs and after any host, channel, 
         or option changes. An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/setupTest
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/setupTest"
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def tearDownTest(self):
        """ Used to remove the test peramitors in the memory of the Mu Test 
        Suite. An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/test/tearDownTest
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/test/tearDownTest"
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def getTestSuiteResults(self):
        """ Used to retrieve the test results of the test suite from the Mu 
        once it has completed a test run. An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/test/runSuite/results
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/test/runSuite/results"
        responseData = self.muConnection.get(url, True)
        self.session = responseData
        if responseData.getRetrievedObjectType() == responseData.JSON and \
            not responseData.hasError():
            jasonData = responseData.getJsonObject()
            tmpResponse = jasonData["testResults"]
            resultsListData = tmpResponse["results"]
            results = []
            for test in resultsListData:
                result = Result(test)
                results.append(result)
                log.debug(result)
            responseData.setRetrievedObject(results)
            responseData.setRetrievedObjectType(responseData.RESULTS_LIST)
        log.debug(responseData)
        return responseData

    def getTestSuiteStatus(self):
        """ Used to retrieve the test suite status of the loaded scenario from
        the Mu. An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/test/runSuite/status
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/test/runSuite/status"
        responseData = self.muConnection.get(url, True)
        self.session = responseData
        log.debug("Dir: %s", dir(responseData))
        log.debug(responseData)
        return responseData

    def runTestSuite(self, uuid):
        """ Used to run a test suite on the loaded scenario on the Mu. An 
        active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/test/runSuite/<uuid>
            
        Arguments:
            uuid - the uuid value of the test suite that resides on the Mu
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/test/runSuite/" + uuid
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def verifyScenario(self):
        """ Used to run a verification on the loaded scenario on the Mu using 
        the options that have been set. An active session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/test/run
            
        Arguments: None
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/test/run"
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        log.debug(responseData)
        return responseData

    def importCSV(self, csvFileName, newTemplateName):
        """ Used to import a csv file to the Mu in preperation of running a 
        test suite. The csv file must be in the correct format. An active 
        session is required.

        Example URL: https://$MU_IP/api/v5/ddt/session/test/import/csv
            
        Arguments:
            csvFileName - the name of the csv file including any path info 
                needed to get there /myPath/myCsvFile.csv
            newTemplateName - this is the name to be placed in the 
                mimeAtachment section.
            
        Returns: MuConnection.MuResponseObject
            
        TODO:
            newTemplateName option is not working
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/test/import/csv"
        responseData = self.muConnection.postMime(url, csvFileName,
                'file', newTemplateName, True)
        self.session = responseData
        if responseData.getRetrievedObjectType() == responseData.JSON and \
            not responseData.hasError():
            jasonData = responseData.getJsonObject()
            tmpResponse = jasonData["response"]
            message = tmpResponse["message"]
            if message.find("Unable") >= 0:
                responseData.setCompleted(False)
        log.debug(responseData)
        return responseData

    def exportCSV(self, UUID, fileToSaveAs=None, fileMode='t'):
        """ Used to export a csv file from the Mu.

        Example URL: https://$MU_IP/api/v5/ddt/session/test/export/csv/<uuid>
            
        Arguments:
            UUID - the uuid of the test suite on the Mu that will be exported
            fileToSaveAs - the path and name to save the csv data to. If not 
                specified the data will only be in the MuResponseObject.
            fileMode - should always be t for text
            
        Returns: MuConnection.MuResponseObject
        """

        url = self.muConnection.TOP_LEVEL_URL + \
            "/api/v5/ddt/session/test/export/csv/" + UUID
        responseData = self.muConnection.post(url, None, None, True)
        self.session = responseData
        if responseData.getRetrievedObjectType() == responseData.JSON and \
            not responseData.hasError():
            jasonData = responseData.getJsonObject()
            tmpResponse = jasonData["response"]
            message = tmpResponse["message"]
            if message.find("Unable") >= 0:
                log.info(message)
                responseData.setCompleted(False)
            elif fileToSaveAs != None:

                newFile = open(fileToSaveAs, "w" + fileMode)
                newFile.write(message)
                newFile.close()
        log.debug(responseData)

        return responseData


class TestCase:

    """ This class will contain the data represented in a singe Mu Studio Verify test case.

        TODO: not implemented.
            
    """

    name = ''

    def __init__(self, jsonData):
        log.warn('TODO')

    def __str__(self):
        log.warn('TODO')


class Result:

    """ This class represents a test result.

    Example JSON structure: 
        {'status': 'Ok', 'testName': '1 Default Values(1)', 
         'testResult': 'PASSED', 'testOptions': {'options': 
             [{'name': 'scenario_user_options.rootDoc', 
               'value': '/services/org'}, 
              {'name': 'scenario_user_options.uri', 'value': '192.168.1.1'}, 
              {'name': 'io.timeout', 'value': 250}, 
              {'name': 'scenario_user_options.responseCode', 'value': 200}]}}
        
    Variables:
        status - the status of the test execution: OK, ????
        testName - the name of the test case.
        testResult - the result of the test: PASSED, FAILED
        testOptions - the options of the test when it ran. unlike the 
            options list in the testSuite this should contain all options 
            and there values.
    
    Methods:
        __init__ - create result based on json data from the mu
        __str__ - print it nice and prety
        loadOptions - load a list of class MuTestApi.option
        load - load test result from passed objects
        set/getStatus - set or get methods for the status
        set/getTestName - set or get methods for the test name
        set/getTestResult - set or get methods for the test result
        set/getTestOptions - set or get methods for the test options
    """

    status = ''
    testName = ''
    testResult = ''
    testOptions = []

    def __init__(self, jsonData):
        self.status = jsonData['status']
        self.testResult = jsonData['testResult']
        if self.testResult == "END":
            return
        self.testName = jsonData['testName']
        tmpdata = jsonData['testOptions']
        tmpdata = tmpdata["options"]
        self.loadOptions(tmpdata)

    def __str__(self):

        return "{ status : " + self.status + " , testName : " + self.testName + \
            ", testResult : " + self.testResult + ", testOptions : [" + \
            (",").join(str(v) for v in self.testOptions) + "]}"

    def loadOptions(self, optionsList):
        self.testOptions = []
        for itum in optionsList:
            option = Option(itum)
            self.testOptions.append(option)

    def load(self, status='', testName='', testResult='', testOptions=[]):
        self.status = status
        self.testName = testName
        self.testResult = testResult
        self.testOptions = testOptions

    def getStatus(self):
        return self.status

    def getTestName(self):
        return self.testName

    def getTestResult(self):
        return self.testResult

    def getTestOptions(self):
        return self.testOptions


class Option:

    """ This class represents a test option.

    Example JSON structure: 
        {'name': 'scenario_user_options.rootDoc', 'value': '/services/org'}
        
    Variables:
        name - the full option name
        value - the option value
    
    Methods:
        __init__ - create result based on json data from the mu
        __str__ - print it nice and prety
        set/getName - set or get methods for the name
        set/getValue - set or get methods for the test value
    """

    name = ''
    value = ''

    def __init__(self, jsonData):
        self.name = jsonData['name']
        self.value = jsonData['value']

    def __str__(self):
        return "{name: " + self.name + ", value: " + str(self.value) + \
            "}"

    def getName(self):
        return self.name

    def getValue(self):
        return self.value

    def setName(self, name):
        self.name = name

    def setValue(self, value):
        self.value = value


